#
#
puts ">>>>>>>>>>>>>>>> Loading LdapUser"

require 'rubygems'
require 'net/ldap'
#require 'models/login_user'
require 'login_user'

class LdapUser < LoginUser
  
#  attr_accessible :ldap_dn
#  let_read :ldap_dn
#  let_write :ldap_dn, :if => :new_record?

  let_read :is_local
  
  def self.ldap_host(host)
    @@host = host
  end
  
  def self.ldap_port(port)
    @@port = port
  end
  @@port = 389  # default
  
  def self.ldap_auth(user, pw)
    @@auth_user = user
    @@auth_pw = pw
  end
  
  def self.ldap_base(base)
    @@base = base
  end

  
  # Log on the user for this record, given a user name and password.
  # Class method.
  def self.sign_on(handle, pwd)
    user = User.find_by_login(handle)
    if user.nil?
      # create a local record for this user and make it the current one
      # TODO: Need to check for save errors
      Thread.current[:tmp_admin] = true  # temporarily become admin
      user = self.create(:login => handle)
      Thread.current[:tmp_admin] = false  
      
      # for some strange reason, the above created user can't be marshalled.
      # However, looking it up in the database creates a kosher object. Go figure.
      user = User.find_by_login(handle)
    end
    if user.nil?
      self.current = nil
      return
    end
    
    # Ensure the user's ldap_dn is current before 
    # proceeding to attempt to authenticate
    lookup_ldap = create_ldap
    filter = Net::LDAP::Filter.eq('samaccountname', handle)
    attrs = [:dn]
    ldap_result = lookup_ldap.search( :base => @@base, :filter => filter, :attributes => attrs)
   
    # update user record with current dn
    Thread.current[:tmp_admin] = true  # temporarily become admin
    user.ldap_dn = String.new(ldap_result.first.dn) #not sure why String.new() is needed explicitly
                                                    #here, ruby gets annoyed if its not
    user.save
    Thread.current[:tmp_admin] = false      
    
    # Proceed with sign on attempt  
  
#    puts user.class
#    puts user.class.superclass
#    puts user.class.superclass.object_id
#    puts user.inspect
#    puts user.methods.sort.inspect
#    @u = user; breakpoint

#    res = user.sign_on(pwd)
    res = user.sign_on(pwd)  # TODO: Figure out why sign_on disappears
    puts ">> after sign_on: #{res}"
    puts user.methods.sort.inspect
    res
  end
  
  # helper function for the above,
  # Creates a lookup ldap instance
  def self.create_ldap
    ldap = Net::LDAP.new
    ldap.host = @@host
    ldap.port = @@port
    ldap.auth @@auth_user, @@auth_pw
    ldap
  end
  
  
  def self.create(params = {})
    if params[:is_local].nil?
      # find 'login' in LDAP server
      login = params[:login] || raise('Missing login name')
      logger.debug "ldap>>> Looking for '#{login}' in LDAP"
      ldap = Net::LDAP.new
      ldap.host = @@host
      ldap.port = @@port
      ldap.auth @@auth_user, @@auth_pw
      logger.debug "ldap>>> LDAP '#{ldap.inspect}'"
      begin
        filter = Net::LDAP::Filter.eq("samaccountname", login)
        attrs = ["dn", "cn", 'mail']
        logger.debug "ldap>>> Searching '#{@@base}'"
        #@ldap = ldap; @f = filter; @b = @@base; @a = attrs; breakpoint
        known = false
        ldap.search( :base => @@base, :filter => filter, :attributes => attrs) do |entry|
          known = true
          entry.each do |attr, vals|
            vals.each do |value|
              logger.debug "ldap>>> #{attr}:#{value}"
              case attr.to_sym
              when :cn
                params[:name] = value
              when :mail
                params[:email] = value
              when :dn
                params['ldap_dn'] = value
              end
            end
          end
        end
      rescue Net::LDAP::LdapError => ex
        raise ex
      end
      raise "Unknown user '#{login}'" unless known
      params[:password] = params[:password_confirmation] = 'not_really'
    end
#    @p = params; breakpoint
    logger.debug "ldap>>> Creating '#{params.inspect}'"
    user = super(params)
    # For some reason the above assignment of ldap_dn is ignored, let's force it.
#    @p = params; breakpoint
    user.ldap_dn = params['ldap_dn']
    user.save
  end
  
  # Return true if the currently-logged-in user is the administrator.
  # To allow for dynamic user creations, we allow a thread to 
  # temporarily assume admin rights if the :tmp_admin is set true
  # in the current thread.
  #
  def self.admin?
    return true if Thread.current[:tmp_admin] == true
    super
  end

  def sign_on(passwd)
    if self.is_local
      return super
    end
    logger.debug "ldap>>> checking credentials for '#{self.login}'"
    ldap = Net::LDAP.new
    ldap.host = @@host
    ldap.port = @@port
    ldap.auth self.ldap_dn, passwd

    # set current to self if ldap bind succeeds, otherwise set nil
    self.class.current = nil
    begin
      self.class.current = ldap.bind ? self : nil
    rescue Net::LDAP::LdapError => ex
      raise ex
    end
    self.class.current
  end
end
puts "LdapUser #{LdapUser.object_id} loaded!!"
